import { MTreeNode } from "@/common/Tree/MTreeNode";
import { ModelFrom, ReadPermissionType, ModelType } from "@/constants/Enums";
import { MetaclassType } from "@/constants/MetaclassType";
import { StType } from "@/constants/StType";
import { IGraphNode } from "@/modules/model/project/IGraphNode";
import { SortNodeUtil } from "@/modules/model/project/SortNodeUtil";
import { getUid, treeForEach } from "@/util/common";
import { modelService, projectService } from "../../../service";
import { modelUtil } from "@/modules/model/util/ModelUtil";
import { CoworkPackageStatus } from "@/modules/model/project/ProjectConfig";
import { coworkService } from "@/modules/cowork/service/CoworkService";
import { ModelData } from "@/constants/types";

export enum IsAccept {
  NOTHING = 0,
  ACCEPT,
  NOT_ACCEPT
}

export enum Status {
  UNMODIFIED = "UNMODIFIED",
  ADD = "ADD",
  DELETED = "DELETE",
  MODIFIED = "MODIFIED"
  // INNERCHANGE = 'INNERCHANGE' // 内部有任何节点有 内部视图有变更, 属性有变化
}

export enum Move {
  NOTHING = 0,
  OUT = 1,
  IN = 2
}

export enum ProjectType {
  ONLINE = 1,
  NORMAL = 0
}

export type MergeModelData = ModelData & {
  isAccept: IsAccept;
  status: Status;
  hasChildNode: boolean;
  mergeFrom: string;
  move: number;
  readOnly: boolean;
  innerChange: boolean; // 内部有任何节点有新增，删除,内部视图有变更
};

export type NodeBackground = {
  background: string;
  status: string;
  id: string;
};

export type SelectedNode = {
  id: string | number;
  ownerId: string;
  key: string;
};

export type MenuDisableConfig = {
  accept: boolean;
  acceptCascade: boolean;
  acceptWithAttribute: boolean;
  reject: boolean;
  rejectCascade: boolean;
  rejectWithAttribute: boolean;
};

export class BuildFakeData implements MergeModelData {
  id_ = 1;
  id = getUid();
  key = "";
  ownerId = "";
  type = ModelType.Node;
  metaclass: MetaclassType | null = MetaclassType.Package;
  sts = [];
  projectId = "";
  name = this.id;
  baseSt: StType | null = null;
  icon = "statics/images/sysml/Model.svg";
  isAccept = IsAccept.ACCEPT;
  status = Status.ADD;
  hasChildNode = true;
  from = ModelFrom.USER_CREATE;
  showAttribute = {};
  mergeFrom = "";
  innerChange = false;
  move = 0;
  readOnly = false;
  constructor(ownerId: string) {
    this.ownerId = ownerId;
  }
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export class MergeTree<T extends MergeModelData> {
  nodeMap = new Map<string, MTreeNode<MergeModelData>>(); // node 索引
  nodeIdMap = new Map<string, MTreeNode<MergeModelData>>(); // node 索引
  parentIndexMap = new Map<MTreeNode["id"], Array<MTreeNode<MergeModelData>>>(); // 父节点id到子节点们的映射
  root: MTreeNode<MergeModelData>;
  maxSize = 2000;
  isSourceOnline = ProjectType.NORMAL;
  onlineSourceRootId = "";
  sourceProjectId = "";
  targetProjectId = "";
  targePackageStatus: { [p: string]: CoworkPackageStatus } = {};
  constructor(root: MergeModelData, sourceProjectId: string, targetProjectId: string) {
    this.sourceProjectId = sourceProjectId;
    this.root = this.buildNode(root, "");
    this.targetProjectId = targetProjectId;
  }
  // 展开节点
  async expandNode(node: MTreeNode<MergeModelData>) {
    // 对于关系节点可能已经计算过了
    if (node.children.length > 0) {
      return;
    }
    node.loaded = false;
    node.loading = true;
    // 请求后端获取节点
    const childNode = await projectService.projectMergeQueryChildren(this.targetProjectId, this.sourceProjectId, node.data.id);
    const children: MTreeNode<MergeModelData>[] = [];
    for (let it of childNode.data) {
      if (it.modelData && !(it.changeState === Status.UNMODIFIED && !it.innerChange)) {
        const param: MergeModelData = {
          ...(it.modelData as ModelData),
          isAccept: it.acceptState,
          status: it.changeState,
          hasChildNode: it.hasChild,
          mergeFrom: it.mergeFrom,
          move: it.move,
          innerChange: it.innerChange,
          ownerId: it.ownerId,
          readOnly: it.readOnly
        };
        const child = this.buildNode(param, node.data.id);
        child.disabled = param.readOnly;
        if (child.id) {
          this.nodeIdMap.set(param.id, child);
        }
        children.push(child);
      }
    }
    node.children = [];
    for (let index = 0; index < children.length; index++) {
      const child = children[index];
      if (child.data.type === ModelType.Relation) {
        let groupNode = this.getNode(child.data.ownerId + "-relations" + node.data.move);
        if (!groupNode) {
          groupNode = this.createRelationGroupNode(child.data.ownerId, child.projectId, node.data.move);
          node.children.push(groupNode);
          const owner_ = this.parentIndexMap.get(child.data.ownerId);
          owner_ && owner_.push(groupNode);
          groupNode.children = [];
        }
        groupNode.children.push(child);
        if (child.data.status !== Status.UNMODIFIED) {
          groupNode.data.innerChange = true;
        }
        const owner = this.parentIndexMap.get(child.data.ownerId + "-relations");
        owner && owner.push(node);
      } else {
        node.children.push(child);
        const owner = this.parentIndexMap.get(child.data.ownerId);
        owner && owner.push(node);
      }
    }
    SortNodeUtil.sortNodeChildren(node.children as unknown as IGraphNode[]);

    node.loaded = true;
    node.loading = false;
  }

  getChildren(node: MTreeNode<MergeModelData>) {
    return this.parentIndexMap.has(node.id) ? this.parentIndexMap.get(node.id) : [];
  }

  getNode(key: string) {
    return this.nodeMap.has(key) ? this.nodeMap.get(key) : null;
  }

  getNodeFromShape(id: string) {
    return this.nodeIdMap.has(id) ? this.nodeIdMap.get(id) : null;
  }

  buildNode(data: MergeModelData, parentId: string) {
    const node = new MTreeNode<MergeModelData>(data);
    node.id = data.id;
    node.parentId = parentId;
    node.isLeaf = !data.hasChildNode;
    node.key = data.id + "." + data.ownerId;
    this.nodeMap.set(node.key, node);
    this.parentIndexMap.set(node.id, new Array<MTreeNode<MergeModelData>>());
    return node;
  }

  async updateNode(nodeId: string[], sourceId: string, targetId: string) {
    const newNodes = await projectService.projectMergeQueryTreeNode(targetId, sourceId, nodeId);
    if (newNodes) {
      newNodes.forEach(node => {
        const key = node.instanceId + "." + node.ownerId;
        const oldNode = this.getNode(key);
        const newNodeData: MergeModelData = {
          ...(node.modelData as ModelData),
          isAccept: node.acceptState,
          status: node.changeState,
          hasChildNode: node.hasChild,
          mergeFrom: node.mergeFrom,
          move: node.move,
          innerChange: node.innerChange,
          ownerId: node.ownerId,
          readOnly: node.readOnly
        };
        if (node.instanceId == this.root.id) {
          newNodeData.status = node.changeState;
        } else if (oldNode) {
          newNodeData.status = oldNode.data.status;
        }
        if (oldNode && oldNode.data.ownerId === newNodeData.ownerId) {
          oldNode.data = newNodeData;
          treeForEach([this.root], child => {
            if (child.id === newNodeData.id && child.data.ownerId === newNodeData.ownerId) {
              child.data = newNodeData;
              child.disabled = newNodeData.readOnly;
            }
          });
        }
      });
    }
  }

  createRelationGroupNode(ownerId: string, projectId: string, moveStatus?: number) {
    const data: MergeModelData = {
      id_: 0,
      projectId: projectId,
      id: ownerId + "-relations" + moveStatus,
      name: "关系", // 模型的name，可能为空
      metaclass: null, // 所应用的metaclass ,
      sts: [], // 应用的stereotype的id数组，暂时不做
      icon: "statics/images/sysml/Relation.svg", // 图标url
      hasChildNode: true, // 是否拥有至少一个子元素 (节点的展开按钮需要用到)
      ownerId: ownerId,
      innerChange: false,
      move: 0,
      isAccept: IsAccept.NOT_ACCEPT,
      mergeFrom: "",
      status: Status.UNMODIFIED,
      type: ModelType.RelationGroup,
      baseSt: null,
      from: ModelFrom.USER_CREATE,
      showAttribute: {},
      readOnly: false
    };
    const node = new MTreeNode<MergeModelData>(data);
    node.id = data.id;
    node.parentId = ownerId;
    node.isLeaf = !data.hasChildNode;
    node.key = data.id + "." + data.ownerId;
    this.nodeIdMap.set(node.id, node);
    this.nodeMap.set(node.id, node);
    this.parentIndexMap.set(node.id, new Array<MTreeNode<MergeModelData>>());
    return node;
  }

  async isModelDisabled(node: MergeModelData) {
    if (this.nodeIdMap.has(node.id)) return;
    if (modelUtil.isPackage(node.metaclass)) {
      if (node.isOnlineNew) {
        return false;
      } else {
        const pkgStatus = this.targePackageStatus[node.id] as CoworkPackageStatus;
        if (pkgStatus?.CHECKED_IN && pkgStatus?.READ_PERMISSION !== ReadPermissionType.R) {
          return false;
        } else {
          return true;
        }
      }
    } else {
      // 如果不是包则要看所在的第一个包的锁定编辑状态
      if (node.ownerId === this.root.id) {
        const pkgStatus = this.targePackageStatus[this.root.id];
        if (pkgStatus?.CHECKED_IN && pkgStatus?.READ_PERMISSION !== ReadPermissionType.R) {
          return false;
        } else {
          return true;
        }
      } else {
        const firstPkg = await modelService.getFirstPkgOwner(node.mergeFrom, node.id);
        if (!firstPkg) {
          return true;
        }
        if (firstPkg.isOnlineNew) {
          return false;
        } else {
          const pkgStatus = this.targePackageStatus[firstPkg.id];
          if (pkgStatus?.CHECKED_IN && pkgStatus?.READ_PERMISSION !== ReadPermissionType.R) {
            return false;
          } else {
            return true;
          }
        }
      }
    }
  }

  async loadPkgStauts() {
    const pkgStatus = await coworkService.getOnlinePkgStatus(this.targetProjectId);
    this.targePackageStatus = pkgStatus;
  }
  /**
   * 节点数量超过一定值时会进行清理，但是目前这个方法存在链式问题
   */
  clean() {
    if (this.parentIndexMap.size <= this.maxSize) return;
    this.parentIndexMap.forEach(children => {
      children.forEach(child => {
        if (!child.expand) this.parentIndexMap.delete(child.id);
      });
    });
    this.maxSize = Math.max(this.parentIndexMap.size, this.maxSize);
  }
}
